package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.apis.wemedia.IChannelClient;
import com.heima.article.mapper.ApArticleConfigMapper;
import com.heima.article.mapper.ApArticleContentMapper;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.service.ApArticleService;
import com.heima.article.service.ArticleFreemarkerService;
import com.heima.common.constants.ArticleConstants;
import com.heima.common.redis.CacheService;
import com.heima.model.article.dtos.ArticleBehaviorDto;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.article.dtos.ArticleHomeDto;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.article.pojos.ApArticleConfig;
import com.heima.model.article.pojos.ApArticleContent;
import com.heima.model.article.vos.HotArticleVo;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.mess.ArticleVisitStreamMess;
import com.heima.model.search.vos.SearchArticleVo;
import com.heima.model.user.pojos.ApUser;
import com.heima.model.wemedia.pojos.WmChannel;
import com.heima.utils.thread.ApThreadLocalUtil;
import org.apache.commons.lang.StringUtils;
import org.checkerframework.checker.units.qual.A;
import org.joda.time.DateTime;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.nio.channels.Channel;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;

@Service
public class ApAritcleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle> implements ApArticleService {

    //指定每页默认显示的条数
    public static final int PAGESIZE = 10;
    //每页显示的最大条数
    public static final int MAX_PAGE_SIZE = 50;

    @Autowired
    private ApArticleMapper apArticleMapper;

    @Autowired
    private ApArticleContentMapper apArticleContentMapper;

    @Autowired
    private ApArticleConfigMapper apArticleConfigMapper;

    @Autowired
    private ArticleFreemarkerService articleFreemarkerService;

    @Autowired
    private IChannelClient channelClient;


    @Autowired
    private CacheService cacheService;

    /**
     * 接收自媒体端的新闻，同步到ap端
     *
     * @param articleDto
     * @return
     */
    @Override
    @Transactional
    public ResponseResult saveArticle(ArticleDto articleDto) {

        if(articleDto==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }

        ApArticle article = new ApArticle();
        BeanUtils.copyProperties(articleDto,article);

        //判断到底是更新还是添加
        if(articleDto.getId()==null){
            //说明是第一次审核后过来进行文章同步
            //1、ap_article添加数据
            this.save(article);
            //2、ap_article_Content添加数据
            ApArticleContent content  = new ApArticleContent();
            content.setContent(articleDto.getContent());
            content.setArticleId(article.getId());
            apArticleContentMapper.insert(content);
            //3、ap_article_config表中添加数据
            ApArticleConfig articleConfig = new ApArticleConfig();
            articleConfig.setArticleId(article.getId());
            articleConfig.setIsDelete(false);
            articleConfig.setIsDown(false);
            articleConfig.setIsForward(true);
            articleConfig.setIsComment(true);
            apArticleConfigMapper.insert(articleConfig);
        }else{
            //说明以前已经同步过，现在做更新
            //1、更新ap_article
            this.updateById(article);
            //2、更新内容
            ApArticleContent content = apArticleContentMapper.selectOne(Wrappers.<ApArticleContent>lambdaQuery().eq(ApArticleContent::getArticleId, articleDto.getId()));
            content.setContent(articleDto.getContent());
            apArticleContentMapper.updateById(content);
        }



        //生成静态页面()
        articleFreemarkerService.buildStaticHtmlToMinio(article,articleDto.getContent());



        return ResponseResult.okResult(article.getId());
    }





    @Override
    public ResponseResult load(Short type, ArticleHomeDto articleHomeDto) {

        //1、参数检测
        //1.1 size:应该介于10~50之间
        Integer size = articleHomeDto.getSize();
        if(size==null||size<=0){
            size= PAGESIZE;
        }
        size=Math.min(MAX_PAGE_SIZE,size);
        articleHomeDto.setSize(size);

        //1.2 tag:如果没有传给默认值"__all__"
        if(StringUtils.isBlank(articleHomeDto.getTag())){
            articleHomeDto.setTag(ArticleConstants.DEFAULT_TAG);
        }

        //1.3 保证日期不能为空
        if(articleHomeDto.getMinBehotTime()==null) {
            articleHomeDto.setMinBehotTime(new Date());
        }
        if(articleHomeDto.getMaxBehotTime()==null) {
            articleHomeDto.setMaxBehotTime(new Date());
        }


        //2、查询
        List<ApArticle> apArticles = apArticleMapper.loadArticleList(type, articleHomeDto);

        return ResponseResult.okResult(apArticles);
    }


    /**
     * 计算近五天所有文章的分值
     */
    @Override
    public void computeArticle() {

        //1、查询出5天内发布的所有文章
        //1.1 获取前五天的日期
        Date date = DateTime.now().minusDays(5).toDate();
        //1.2 查出近5天所有的文章
        List<ApArticle> apArticles = apArticleMapper.loadArticleByLast5Days(date);

        //1.3 近5天内没有文章，则返回
        if(apArticles==null|| apArticles.size()==0){
            return;
        }

        //2、给5天所有的文章算分
        List<HotArticleVo> hotArticleVoList= computeScore(apArticles);


        //3、查询出所有的频道
        ResponseResult<List<WmChannel>> response = channelClient.findChannels();


        //4、遍历所有的频道，再从文章赛选出对应频道的文章
        if(response.getCode()==200){
            List<WmChannel> channels = response.getData();
            if(channels!=null&&channels.size()>0){
                for (WmChannel channel : channels) {
                    //存放当前频道的文章信息
                    List<HotArticleVo> collect = hotArticleVoList.stream().filter(hotArticleVo -> hotArticleVo.getChannelId() == channel.getId()).collect(Collectors.toList());
                    //对文章按照分值进行排序
                    collect= collect.stream().sorted((o1, o2) -> o2.getScore()-o1.getScore()).collect(Collectors.toList());
                    //如果文章超过30条，则只取30条
                    if(collect.size()>30){
                        collect=collect.subList(0,29);
                    }
                    //将当前频道的热门文章，缓存到redis中  hot_article_first_page_1    [{},{}]
                    cacheService.set(ArticleConstants.HOT_ARTICLE_FIRST_PAGE+channel.getId(),JSON.toJSONString(collect));
                }
            }
        }

        //5、计算推荐频道的热门数据
        List<HotArticleVo> collect = hotArticleVoList.stream().sorted((o1, o2) -> o2.getScore() - o1.getScore()).collect(Collectors.toList());
        //如果文章超过30条，则只取30条
        if(collect.size()>30){
            collect=collect.subList(0,29);
        }
        //将当前频道的热门文章，缓存到redis中  hot_article_first_page_all    [{},{}]
        cacheService.set(ArticleConstants.HOT_ARTICLE_FIRST_PAGE+ArticleConstants.DEFAULT_TAG,JSON.toJSONString(collect));


    }

    private   List<HotArticleVo> computeScore(List<ApArticle> apArticles) {
        List<HotArticleVo> hotArticleVoList = new ArrayList<>();

        for (ApArticle apArticle : apArticles) {

            int score=0;

            if(apArticle.getViews()!=null){
                score+=apArticle.getViews();
            }

            if(apArticle.getLikes()!=null){
                score+=apArticle.getLikes()*ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;
            }

            if(apArticle.getComment()!=null){
                score+=apArticle.getComment()*ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;
            }

            if(apArticle.getCollection()!=null){
                score+=apArticle.getCollection()*ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;
            }

            HotArticleVo hotArticleVo= new HotArticleVo();
            BeanUtils.copyProperties(apArticle,hotArticleVo);
            hotArticleVo.setScore(score);

            hotArticleVoList.add(hotArticleVo);
        }

        return hotArticleVoList;
    }


    /**
     * 从缓存中加载首页信息
     *
     * @param articleHomeDto
     * @return
     */
    @Override
    public ResponseResult load2(ArticleHomeDto articleHomeDto) {

        if(articleHomeDto==null || articleHomeDto.getTag()==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }

        String key = ArticleConstants.HOT_ARTICLE_FIRST_PAGE+articleHomeDto.getTag();
        String json = cacheService.get(key);

        if(StringUtils.isNotBlank(json)){
            List<HotArticleVo> hotArticleVoList = JSON.parseArray(json, HotArticleVo.class);
            return ResponseResult.okResult(hotArticleVoList);
        }

        //如果缓存中没有数据则查询数据库
        return load(ArticleConstants.LOADTYPE_LOAD_MORE,articleHomeDto);
    }


    /**
     * 加载用户行为
     *
     * @param articleHomeDto
     * @return
     */
    @Override
    public ResponseResult loadArticleBehavior(ArticleBehaviorDto articleHomeDto) {

        if(articleHomeDto==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }

        ApUser apUser = ApThreadLocalUtil.get();
        if(apUser==null){
            return  ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }


        boolean islike=false,isunlike=false,isCollection=false,isfollow=false,isforward=false;

        Object like = cacheService.hGet("LIEK_BEHAVIOR_" + articleHomeDto.getArticleId(), apUser.getId() + "");
        if(like!=null){
            islike=true;
        }

        Object collection = cacheService.hGet("COLLECTION_BEHAVIOR_" + articleHomeDto.getArticleId(), apUser.getId() + "");
        if(collection!=null){
            isCollection=true;
        }

        Map map = new HashMap<>();
        map.put("islike",islike);
        map.put("isunlike",isunlike);
        map.put("iscollection",isCollection);
        map.put("isfollow",isfollow);
        map.put("isforward",isforward);

        return ResponseResult.okResult(map);
    }


    /**
     * 更新用户行为的次数
     *
     * @param streamMess
     */
    @Override
    public void updateBehavior(ArticleVisitStreamMess streamMess) {

        //1、更新用户行为的次数
        ApArticle article = this.getById(streamMess.getArticleId());

        int likes = article.getLikes()==null?streamMess.getLike():article.getLikes()+streamMess.getLike();
        int views = article.getViews()==null?streamMess.getView():article.getViews()+streamMess.getView();
        int comment = article.getComment()==null?streamMess.getComment():article.getComment()+streamMess.getComment();
        int collection = article.getCollection()==null?streamMess.getCollect():article.getCollection()+streamMess.getCollect();

        article.setLikes(likes);
        article.setCollection(collection);
        article.setComment(comment);
        article.setViews(views);

        this.updateById(article);



        //2、更新缓存中的数据
        int score= 0;
        if(article.getViews()!=null){
            score+=article.getViews();
        }
        if(article.getLikes()!=null){
            score+=article.getLikes()*ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;
        }
        if(article.getComment()!=null){
            score+=article.getComment()*ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;
        }
        if(article.getCollection()!=null){
            score+=article.getCollection()*ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;
        }
        score*=3;   //由于是实时计算，整体分值乘以3


        HotArticleVo hotArticleVo = new HotArticleVo();
        BeanUtils.copyProperties(article,hotArticleVo);
        hotArticleVo.setScore(score);



        //2.2更新当前频道缓存
        refreshRedis( hotArticleVo,hotArticleVo.getChannelId().toString());

        //2.3更新推荐频道的缓存
        refreshRedis(hotArticleVo,ArticleConstants.DEFAULT_TAG);


    }

    private void refreshRedis( HotArticleVo hotArticleVo,String channelId) {
        String s = cacheService.get(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + channelId);

        if(StringUtils.isNotBlank(s)){
            List<HotArticleVo> hotArticleVoList = JSON.parseArray(s, HotArticleVo.class);

            boolean flag=true;

            for (HotArticleVo articleVo : hotArticleVoList) {
                if(articleVo.getId().equals(hotArticleVo.getId())){
                    //如果当前文章在缓存中，则更新缓存文章的分值
                    articleVo.setScore(hotArticleVo.getScore());
                    //文章要重新排序后，重新放回到缓存中
                    hotArticleVoList= hotArticleVoList.stream().sorted((o1, o2) -> o2.getScore()-o1.getScore()).collect(Collectors.toList());
                    cacheService.set(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + channelId,JSON.toJSONString(hotArticleVoList));
                    flag=false;
                    break;
                }
            }

            if(flag){   //如果文章在缓存中不存在

                   if(hotArticleVoList.size()<30){  //如果缓存中文章的数量小于30，则将当前文章直接加到缓存中，并排序后重新放到redis中
                       hotArticleVoList.add(hotArticleVo);
                       hotArticleVoList= hotArticleVoList.stream().sorted((o1, o2) -> o2.getScore()-o1.getScore()).collect(Collectors.toList());
                       cacheService.set(ArticleConstants.HOT_ARTICLE_FIRST_PAGE +channelId,JSON.toJSONString(hotArticleVoList));

                   }else{   //如果缓存中文章的数量大于等于了30，则需要将分值最小的那篇文章取出来，和当前文章的分值作比较。如果当前文章的分值大于最后那篇文章的分值，则将最后那篇文章移除，将当前文章添加进去。否则不作处理
                       //获取最后一篇文章
                       HotArticleVo lastHotArticle = hotArticleVoList.get(hotArticleVoList.size() - 1);
                       //如果最后一篇文章的分值，小于了当前文章的分值
                       if(lastHotArticle.getScore()< hotArticleVo.getScore()){
                            hotArticleVoList.remove(lastHotArticle);  //移除最后一篇文章
                           hotArticleVoList.add(hotArticleVo);  //将当前文章加到缓存中
                           hotArticleVoList= hotArticleVoList.stream().sorted((o1, o2) -> o2.getScore()-o1.getScore()).collect(Collectors.toList());
                           cacheService.set(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + channelId,JSON.toJSONString(hotArticleVoList));
                       }
                   }
            }


        }
        else{
            //如果当前频道没有缓存，则直接把当前文章放到组缓存中
            List<HotArticleVo> hotArticleVoList = new ArrayList<>();
            hotArticleVoList.add(hotArticleVo);
            cacheService.set(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + channelId,JSON.toJSONString(hotArticleVoList));
        }
    }
}
